﻿using Kingdee.BOS;
using Kingdee.BOS.App.Data;
using Kingdee.BOS.Core;
using Kingdee.BOS.Core.DynamicForm.PlugIn;
using Kingdee.BOS.Core.DynamicForm.PlugIn.Args;
using Kingdee.BOS.Orm.DataEntity;
using Kingdee.BOS.Util;
using LM.YF.K3Cloud.Plugin.Models;
using LM.YF.K3Cloud.Plugin.Models.Grasses;
using NumberHelper;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace LM.YF.K3Cloud.Plugin
{
    /// <summary>
    /// 计算实际运费并反写到货运单和发货通知单（挂载到应付单）
    /// <author>鹿奔</author>
    /// <date>240118</date>
    /// </summary>
    [HotUpdate, Description("计算实际运费并反写到货运单和发货通知单（挂载到应付单）")]
    public class CalcActualFreightOperationServicePlugin : AbstractOperationServicePlugIn
    {
        const string ruleId1 = "97952d76-d612-9c12-11ee-b4fd4363ba0b";// 一对一单据转换
        const string ruleId2 = "5a27b083-1a40-405b-85ab-6e0206952a14";// 汇总单据转换

        #region 字段配置

        internal readonly ElementKeySchema FEntity = new ElementKeySchema
        {
            DbKey = "T_AP_PAYABLEENTRY",
            FormKey = "FEntityDetail",
            OrmKey = "AP_PAYABLEENTRY"
        };

        ///// <summary>
        ///// 运费计价金额
        ///// </summary>
        //internal readonly ElementKeySchema F_money = new ElementKeySchema
        //{
        //    DbKey = "F_MONEY",
        //    FormKey = "F_money",
        //    OrmKey = "F_money"
        //};

        /// <summary>
        /// 物料重量
        /// </summary>
        internal readonly ElementKeySchema F_material_weight = new ElementKeySchema
        {
            DbKey = "F_MATERIAL_WEIGHT",
            FormKey = "F_material_weight",
            OrmKey = "F_material_weight"
        };

        /// <summary>
        /// 下推模式：0=一对一下推；1=汇总下推；2=进仓费结算
        /// </summary>
        internal readonly ElementKeySchema F_RDLV_SumPush = new ElementKeySchema
        {
            DbKey = "F_RDLV_SUMPUSH",
            FormKey = "F_RDLV_SumPush",
            OrmKey = "F_RDLV_SumPush"
        };

        /// <summary>
        /// 立账类型：1=业务应付；2=暂估应付；3=财务应付；
        /// </summary>
        internal readonly ElementKeySchema FSetAccountType = new ElementKeySchema
        {
            DbKey = "FSETACCOUNTTYPE",
            FormKey = "FSetAccountType",
            OrmKey = "FSetAccountType"
        };

        /// <summary>
        /// 单据头-价税合计
        /// </summary>
        internal readonly ElementKeySchema FALLAMOUNTFOR = new ElementKeySchema
        {
            DbKey = "FALLAMOUNTFOR",
            FormKey = "FALLAMOUNTFOR",
            OrmKey = "FALLAMOUNTFOR"
        };

        /// <summary>
        /// 单据头-单据编号
        /// </summary>
        internal readonly ElementKeySchema FBillNo = new ElementKeySchema
        {
            DbKey = "FBILLNO",
            FormKey = "FBillNo",
            OrmKey = "BillNo"
        };

        /// <summary>
        /// 单据体-交货数量
        /// </summary>
        readonly ElementKeySchema F_deliver_qty = new ElementKeySchema
        {
            DbKey = "F_deliver_qty",
            FormKey = "F_DELIVER_QTY",
            OrmKey = "F_deliver_qty"
        };

        #endregion

        public override void OnPreparePropertys(PreparePropertysEventArgs e)
        {
            base.OnPreparePropertys(e);

            //e.FieldKeys.Add(F_money.FormKey);
            e.FieldKeys.Add(F_material_weight.FormKey);
            e.FieldKeys.Add(F_RDLV_SumPush.FormKey);
            e.FieldKeys.Add(FSetAccountType.FormKey);
            e.FieldKeys.Add(FALLAMOUNTFOR.FormKey);
            e.FieldKeys.Add(FBillNo.FormKey);
        }

        public override void BeforeExecuteOperationTransaction(BeforeExecuteOperationTransaction e)
        {
            base.BeforeExecuteOperationTransaction(e);
            if (FormOperation.OperationId == Kingdee.BOS.Core.Metadata.FormElement.FormOperation.Operation_Audit)
            {
                AddFreight(e);
            }
            else if (FormOperation.OperationId == Kingdee.BOS.Core.Metadata.FormElement.FormOperation.Operation_UnAudit)
            {
                SubFreight(e);
            }
        }

        /// <summary>
        /// 累加实际运费
        /// </summary>
        /// <param name="e"></param>
        private void AddFreight(BeforeExecuteOperationTransaction e)
        {
            var rows = e.SelectedRows;
            var failureList = new List<string>();
            foreach (var row in rows)
            {
                var failure = AddFreight(row);
                if (failure != null)
                {
                    failureList.Add(failure);
                }
            }
            if (failureList.Count > 0)
            {
                throw new KDBusinessException(null, $"{string.Join("\r\n", failureList)}");
            }
        }

        private string AddFreight(ExtendedDataEntity row)
        {
            var pushType = row.DataEntity[F_RDLV_SumPush.OrmKey]?.ToString();
            var accountType = row.DataEntity[FSetAccountType.OrmKey]?.ToString();

            #region 暂时作废的代码

            if ("0".Equals(pushType) && false)// 功能作废，加个 恒false
            {
                // 一对一转换反写
                var entries = row.DataEntity[FEntity.OrmKey] as DynamicObjectCollection;
                //获取整单重量
                var weightSum = entries.Sum(i => Convert.ToDecimal(i[F_material_weight.OrmKey]));
                for (int i = 0, l = entries.Count; i < l; i++)
                {
                    // 获取应付单分录数据
                    var entry = entries[i];
                    var entryId = Convert.ToInt32(entry["Id"]);
                    var weight = Convert.ToDecimal(entry[F_material_weight.OrmKey]);
                    var weightRate = weightSum == 0 ? 0 : (weight / weightSum);
                    var freight = Convert.ToDecimal(row.DataEntity[FALLAMOUNTFOR.OrmKey]);
                    // 根据运费占比计算总运费
                    var freightSum = weightRate == 0 ? 0 : (freight / weightRate);
                    // 获取发货通知单分录ID
                    var noticeEntryId = DBUtils.ExecuteScalar(Context, $@"select a.FENTRYID
                            from T_SAL_DELIVERYNOTICEENTRY as a-- 发货通知单分录
                            inner join (
                                -- 查询指定明细的物料内码
                                select a2.F_RDLV_MATERIAL, b2.FID
                                from RDLV_t_DiliveOrdEntry as a2-- 货运单分录
                                inner join T_SAL_DELIVERYNOTICE as b2 on b2.FBILLNO = a2.F_RDLV_NOTICENO-- 发货通知单
                                where FENTRYID = (
                                    -- 根据关联关系获取应付单上游单据=货运单明细
                                    select FSID
                                    from T_AP_PAYABLE_LK
                                    where FRULEID = '{ruleId1}'
                                    and FENTRYID = {entryId}
                                )
                            ) as b on b.FID = a.FID and b.F_RDLV_MATERIAL = a.FMATERIALID;", 0);
                    //DBUtils.Execute(Context, $@"update T_SAL_DELIVERYNOTICEENTRY
                    //    set F_ACTUAL_FREIGHT = least(F_ACTUAL_FREIGHT + {freight}, {freightSum})
                    //    where FENTRYID = {noticeEntryId};");
                    DBUtils.Execute(Context, $@"update T_SAL_DELIVERYNOTICEENTRY
                            set F_ACTUAL_FREIGHT = case
                                when F_ACTUAL_FREIGHT + {freight} < {freightSum} then F_ACTUAL_FREIGHT + {freight}
                                else {freightSum}
                            end
                            where FENTRYID = {noticeEntryId};");
                }
            }
            else

            #endregion

            if ("1".Equals(pushType) && "2".Equals(accountType))
            {
                // 汇总下推且暂估应付

                // 运费计算结果
                var entryFreightList = new List<aa2tab>();

                var entries = row.DataEntity[FEntity.OrmKey] as DynamicObjectCollection;
                for (int i = 0, l = entries.Count; i < l; i++)
                {
                    var entry = entries[i];

                    var yfdEntryId = Convert.ToInt32(entry["Id"]);
                    // 本次结算运费
                    var yfdFreight = Convert.ToDouble(row.DataEntity[FALLAMOUNTFOR.OrmKey]);

                    // 应付单结算的产品数量（即汇总下推的总数量）
                    var yfdQty = Convert.ToDouble(entry[F_deliver_qty.OrmKey]);
                    if (yfdFreight == 0 || yfdQty == 0)
                    {
                        var billNo = row.DataEntity[FBillNo.OrmKey]?.ToString();
                        // 如果数量为0或运费为0，则为异常情况，不算了
                        return $"应付单【{billNo}】的价税合计或其第{i + 1}条分录的交货数量为0，无法计算。";
                    }

                    var failure = CalcFreight(yfdEntryId, yfdFreight, yfdQty, freightData =>
                    {
                        entryFreightList.Add(freightData);
                    });
                    if (failure != null)
                    {
                        var billNo = row.DataEntity[FBillNo.OrmKey]?.ToString();
                        return $"应付单【{billNo}】的第{i + 1}条分录的运费计算失败：{failure}。";
                    }
                }

                // 统一更新计算结果
                entryFreightList.ForEach(entryFreightData =>
                {
                    // 累加实际运费
                    var currFreight = DBUtils.ExecuteScalar(Context, $@"select F_RDLV_ACTAMT--实际运费
                        from RDLV_t_DiliveOrdEntry
                        where FENTRYID = {entryFreightData.HydEntryId};", 0M);
                    var sumFreight = currFreight + entryFreightData.HydFreight;
                    // 更新货运单实际运费
                    DBUtils.Execute(Context, $@"update RDLV_t_DiliveOrdEntry
                        set F_RDLV_ACTAMT = {sumFreight}
                        where FENTRYID = {entryFreightData.HydEntryId};");
                    // 更新发货通知单实际运费
                    DBUtils.Execute(Context, $@"update T_SAL_DELIVERYNOTICEENTRY
                        set F_ACTUAL_FREIGHT = {sumFreight}
                        where FENTRYID = {entryFreightData.FhtzdEntryId};");
                });
            }

            return null;
        }

        /// <summary>
        /// 累减实际运费
        /// </summary>
        /// <param name="e"></param>
        private void SubFreight(BeforeExecuteOperationTransaction e)
        {
            var rows = e.SelectedRows;
            var failureList = new List<string>();
            foreach (var row in rows)
            {
                var failure = SubFreight(row);
                if (failure != null)
                {
                    failureList.Add(failure);
                }
            }
            if (failureList.Count > 0)
            {
                throw new KDBusinessException(null, $"{string.Join("\r\n", failureList)}");
            }
        }

        private string SubFreight(ExtendedDataEntity row)
        {
            var pushType = row.DataEntity[F_RDLV_SumPush.OrmKey]?.ToString();
            var accountType = row.DataEntity[FSetAccountType.OrmKey]?.ToString();
            if (!"1".Equals(pushType) || !"2".Equals(accountType))
            {
                return null;
            }
            // 汇总下推且暂估应付

            // 运费计算结果
            var entryFreightList = new List<aa2tab>();

            var entries = row.DataEntity[FEntity.OrmKey] as DynamicObjectCollection;
            for (int i = 0, l = entries.Count; i < l; i++)
            {
                var entry = entries[i];

                var yfdEntryId = Convert.ToInt32(entry["Id"]);
                // 本次结算运费
                var yfdFreight = Convert.ToDouble(row.DataEntity[FALLAMOUNTFOR.OrmKey]);

                // 应付单结算的产品数量（即汇总下推的总数量）
                var yfdQty = Convert.ToDouble(entry[F_deliver_qty.OrmKey]);
                if (yfdFreight == 0 || yfdQty == 0)
                {
                    // 如果重量为0或运费为0，则为异常情况，不算了
                    var billNo = row.DataEntity[FBillNo.OrmKey]?.ToString();
                    // 如果重量为0或运费为0，则为异常情况，不算了
                    return $"应付单【{billNo}】的价税合计或其第{i + 1}条分录的交货数量为0，无法计算。";
                }

                var failure = CalcFreight(yfdEntryId, yfdFreight, yfdQty, freightData =>
                {
                    entryFreightList.Add(freightData);
                });
                if (failure != null)
                {
                    var billNo = row.DataEntity[FBillNo.OrmKey]?.ToString();
                    return $"应付单【{billNo}】的第{i + 1}条分录的运费计算失败：{failure}。";
                }
            }

            // 统一更新计算结果
            entryFreightList.ForEach(entryFreightData =>
            {
                // 累减实际运费
                var currFreight = DBUtils.ExecuteScalar(Context, $@"select F_RDLV_ACTAMT--实际运费
                    from RDLV_t_DiliveOrdEntry
                    where FENTRYID = {entryFreightData.HydEntryId};", 0M);
                var sumFreight = currFreight - entryFreightData.HydFreight;
                sumFreight = sumFreight > 0 ? sumFreight : 0;
                // 更新货运单实际运费
                DBUtils.Execute(Context, $@"update RDLV_t_DiliveOrdEntry
                    set F_RDLV_ACTAMT = {sumFreight}
                    where FENTRYID = {entryFreightData.HydEntryId};");
                // 更新发货通知单实际运费
                DBUtils.Execute(Context, $@"update T_SAL_DELIVERYNOTICEENTRY
                    set F_ACTUAL_FREIGHT = {sumFreight}
                    where FENTRYID = {entryFreightData.FhtzdEntryId};");
            });

            return null;
        }

        private string CalcFreight(int yfdEntryId, double yfdFreight, double yfdQty, Action<aa2tab> callback)
        {
            // 获取该分录的上游货运单分录ID
            var hyEntryIdCollection = DBUtils.ExecuteDynamicObject(Context, $@"select FSID
                from T_AP_PAYABLE_LK
                where FRULEID = '{ruleId2}'
                and FENTRYID = {yfdEntryId}");
            if (hyEntryIdCollection.Count() == 0)
            {
                // 没有上游单据也不正常，不算了
                return "未获取到该应付单的上游单据，无法计算";
            }

            // 一条应付单分录可能对应多条货运单分录
            foreach (var hydEntryIdData in hyEntryIdCollection)
            {
                var hydEntryId = Convert.ToInt32(hydEntryIdData["FSID"]);
                // 获取货运单分录数据
                var hydEntryDataCollection = DBUtils.ExecuteDynamicObject(Context, $@"/*dialect*/
                select F_RDLV_SOURCEENTRYID/*源单分录ID*/, F_RDLV_SALEQTY/*交货数量（基本单位）*/
                from RDLV_t_DiliveOrdEntry
                where FENTRYID = {hydEntryId};");
                var hydEntryData = hydEntryDataCollection.First();
                // 如结算单60块，是两个货运单加起来的，要分摊给两个货运单，按两个货运单各自的数量比例
                var hydQty = Convert.ToDouble(hydEntryData["F_RDLV_SALEQTY"]);
                var hydFractionQty = Fraction.FromDouble(hydQty);
                var yfdFractionQty = Fraction.FromDouble(yfdQty);
                var rate = hydFractionQty / yfdFractionQty;
                var yfdFractionFreight = Fraction.FromDouble(yfdFreight);
                Fraction hydFreight = yfdFractionFreight * rate;
                var hydFreightValue = Math.Round((decimal)hydFreight.Numerator / hydFreight.Denominator);

                // 回调，将计算好的运费累加或累减
                var fhtzdEntryId = Convert.ToInt32(hydEntryData["F_RDLV_SOURCEENTRYID"]);
                callback?.Invoke(new aa2tab
                {
                    HydEntryId = hydEntryId,
                    // 发货通知单分录和货运单分录一一对应
                    FhtzdEntryId = fhtzdEntryId,
                    HydFreight = hydFreightValue
                });
            }

            return null;
        }
    }
}
